﻿using System;
#if DEBUG
using System.Diagnostics;
#endif // #if DEBUG
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace FRL.DebtChased.Silverlight.Controls
{

	/// <summary>
	/// Declaration of ExTextBox class
	/// </summary>
	public class ExTextBox : TextBox
	{

		/////////////////////////////////////////////////////////////
		// Constants and Enumerations

		public enum eFilter
		{
			NotSet = 0,
			PhoneNumber,
			EmailAddress,
			SortCode,
			AccountNumber,
			IBANNumber,
			SwiftCode
		}

		public enum eLetterCase
		{
			NotSet = 0,
			Upper,
			Lower
		}

		/////////////////////////////////////////////////////////////
		// Attributes

		private int m_iMinChars = 0;
		public int MinChars
		{
			get { return m_iMinChars; }
			set { m_iMinChars = value; }
		}

		private eFilter m_filter = eFilter.NotSet;
		public eFilter Filter 
		{
			get { return m_filter; }
			set { 
				m_filter = value;
				_processTextContent();
			}
		}

		private eLetterCase m_letterCase = eLetterCase.NotSet;
		public eLetterCase LetterCase
		{
			get { return m_letterCase; }
			set { 
				m_letterCase = value;
				_processTextContent();
			}
		}

		private static Brush m_brushOriginalBackground;

		private static Brush m_brushErrorBackground;

		private bool m_bBlockTextChangedEvent = false;

		/////////////////////////////////////////////////////////////
		// Construction

		static ExTextBox()
		{

			// Initialize member variables
			m_brushErrorBackground = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xE0, 0xE0));
			m_brushOriginalBackground = new SolidColorBrush(Colors.White);

		}

		public ExTextBox()
		{

			// Initialize member variables			

			// Set up event handlers
			base.TextChanged += new TextChangedEventHandler(ExTextBox_TextChanged);
			base.Loaded += new RoutedEventHandler(ExTextBox_Loaded);
			base.KeyDown += new KeyEventHandler(ExTextBox_KeyDown);

		}

		/////////////////////////////////////////////////////////////
		// Event Handlers

		void ExTextBox_Loaded(object sender, RoutedEventArgs e)
		{

			// Setup colours
			ExTextBox_TextChanged(null, null);

		}

		void ExTextBox_TextChanged(object sender, TextChangedEventArgs e)
		{

			// Shortcuts
			if (m_bBlockTextChangedEvent)
				return;

			// Process the new text
			_processTextContent();

			// If min chars is active...
			if (m_iMinChars != 0)
			{

				// Color the background
				if (base.Text.Length < m_iMinChars)
					base.Background = m_brushErrorBackground;
				else
					base.Background = m_brushOriginalBackground;

			}

		}

		void ExTextBox_KeyDown(object sender, KeyEventArgs e)
		{

			// Filter the key
			if(!_isValidKey(e.Key))
				e.Handled = true;
			
		}

		/////////////////////////////////////////////////////////////
		// Operations

		private bool _isValidKey(Key key)
		{

			// Shortcuts
			if ((key == Key.Back) || (key == Key.Delete) || (key == Key.Ctrl) || (key == Key.Shift))
				return true;
			else if (this.Filter == eFilter.NotSet)
				return true;

			// Declare return variable
			bool bFtmp = true;

			// Compare filters
			if (this.Filter == eFilter.AccountNumber)
			{

				// Block letters
				if (_isKeyLetter(key))
					bFtmp = false;

			}
			else if (this.Filter == eFilter.PhoneNumber)
			{

				// Block letters
				if (_isKeyLetter(key))
					bFtmp = false;

			}
			else if (this.Filter == eFilter.EmailAddress)
			{

				// Allow everything
				bFtmp = true;

			}
			else if (this.Filter == eFilter.SortCode)
			{

				// Block letters
				if (_isKeyLetter(key))
					bFtmp = false;

			}
			else if (this.Filter == eFilter.IBANNumber)
			{

				// Allow letters and numbers
				bFtmp = (_isKeyLetter(key) || _isKeyNumber(key));

			}
			else if (this.Filter == eFilter.SwiftCode)
			{

				// Allow letters and numbers
				bFtmp = (_isKeyLetter(key) || _isKeyNumber(key));

			}
#if DEBUG
			else
				Debug.Assert(false);
#endif // #if DEBUG

			// Return variable
			return bFtmp;

		}

		private bool _isKeyLetter(Key key)
		{

			// Check letters
			return 
			(
				((key >= Key.A) && (key <= Key.Z))
			);

		}

		private bool _isKeyNumber(Key key)
		{

			// Check letters
			return 
			(
				(Keyboard.Modifiers == ModifierKeys.None)
				&&
				(
					((key >= Key.D0) && (key <= Key.D9))
					||
					((key >= Key.NumPad0) && (key > Key.NumPad9))
				)
			);

		}

		private bool _isValidChar(char cChar)
		{

			// Shortcuts
			if (this.Filter == eFilter.NotSet)
				return true;

			// Declare return variable
			bool bFtmp = true;

			// Compare filters
			if (this.Filter == eFilter.AccountNumber)
			{

				// Allow numbers
				bFtmp = char.IsNumber(cChar);

			}
			else if (this.Filter == eFilter.PhoneNumber)
			{

				// Allow numbers, space and +
				bFtmp = ( char.IsNumber(cChar) || (cChar==' ') || (cChar=='+') );

			}
			else if (this.Filter == eFilter.EmailAddress)
			{

				// Allow numbers, letters, @, -, _ ,.
				bFtmp = ( char.IsNumber(cChar) || char.IsLetter(cChar) || (cChar=='@') || (cChar=='-') || (cChar=='_') || (cChar=='.'));

			}
			else if (this.Filter == eFilter.SortCode)
			{

				// Allow numbers, space and -
				bFtmp = ( char.IsNumber(cChar) || (cChar=='-') );

			}
			else if (this.Filter == eFilter.IBANNumber)
			{

				// Allow numbers and letters
				bFtmp = ( char.IsNumber(cChar) || char.IsLetter(cChar) );

			}
			else if (this.Filter == eFilter.SwiftCode)
			{

				// Allow numbers and letters
				bFtmp = ( char.IsNumber(cChar) || char.IsLetter(cChar) );

			}
#if DEBUG
			else
				Debug.Assert(false);
#endif // #if DEBUG

			// Return variable
			return bFtmp;

		}

		private void _processTextContent()
		{

			// If there is a filter, or letter-case...
			if (
				(base.Text.Length > 0)
				&&
				(
					(this.Filter != eFilter.NotSet)
					||
					(this.LetterCase != eLetterCase.NotSet)
				)
			)
			{

				// Build new string
				int iSelectionStart = base.SelectionStart;
				int iSelectionLength = base.SelectionLength;
				bool bDelta = false;
				StringBuilder sb = new StringBuilder(base.Text);

				// Loop through characters
				int iOffset = 0;
				while (iOffset < sb.Length)
				{
					if (_isValidChar(sb[iOffset]))
					{

						// Convert case
						char c1 = sb[iOffset];
						if (LetterCase == eLetterCase.Upper)
							c1 = char.ToUpper(c1);
						else if (LetterCase == eLetterCase.Lower)
							c1 = char.ToLower(c1);
						if (c1 != sb[iOffset])
						{
							sb[iOffset] = c1;
							bDelta = true;
						}

						// Move on
						iOffset++;

					}
					else
					{
						sb.Remove(iOffset, 1);
						if (iOffset < iSelectionStart)
							iSelectionStart--;
						else if ((iOffset >= iSelectionStart) && (iOffset <= (iSelectionStart + iSelectionLength)))
							iSelectionLength--;
						bDelta = true;
					}
				} // while-loop

				// If changed...
				if (bDelta)
				{
					m_bBlockTextChangedEvent = true;
					{
						base.Text = sb.ToString();
					}
					m_bBlockTextChangedEvent = false;
					this.SelectionStart = iSelectionStart;
					iSelectionLength = Math.Min(iSelectionLength, base.Text.Length);
					iSelectionLength = Math.Max(iSelectionLength, 0);
					this.SelectionLength = iSelectionLength;
				}

			} 

		}

	}

}
